package com.nannybear.Communication;

import android.os.Handler;
import android.util.Log;
import android.view.View;

import com.nannybear.MainActivity;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.Socket;

/**
 * \brief Connection with NannyBear to send and receive messages. Treat the connection state.
 * \author Audrey LOISEAU
 * \date 30-03-2015
 */

/********************************* COPYRIGHT *********************************
* Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************************/

public class Connection{
    /********** DECLARATIONS **********/
    // Variables
    private Socket socket=null;
    private boolean isConnected;
    private BufferedReader reader=null;
    private BufferedWriter writer=null;

    // Classes
    private ProxyRobot proxyRobot;
    private Postman postman;
    private final MainActivity activity;
    private final String ip;
    private final int port;

    //Handler
    private final Handler myHandler = new Handler();
    private final Handler myTimer = new Handler();

    /************ METHODS ************/
    /**
     * \brief Constructor and initialization of parameters
     * \param activity  The activity
     * \param ip    The NannyBear IP
     * \param port  The NannyBear port
     */
    public Connection(MainActivity activity, String ip, int port){
        this.activity = activity;
        this.ip = ip;
        this.port = port;
        isConnected = false;
        startConnection(true);
    }

    /**
     * \brief Start the connection by calling an asyncTask
     * \param firstConnection   True if it is the first connection
     */
    public void startConnection(boolean firstConnection){
        ConnectionTask connectionTask = new ConnectionTask(this, ip, port, firstConnection);
        connectionTask.execute();
    }

    /**
     * \brief Link observers of ProxyRobot and CriticalityThresholds
     */
    public void linkObserverToSubject(){
        this.getActivity().getLightThreshold().addObserver(this.getProxyRobot());
        this.getActivity().getSoundThreshold().addObserver(this.getProxyRobot());
        this.getActivity().getHumidityThreshold().addObserver(this.getActivity().getViewPuppeteer().getCriticalityThresholds());
        this.getActivity().getTemperatureThreshold().addObserver(this.getActivity().getViewPuppeteer().getCriticalityThresholds());
    }

    /**
     * \brief Send a message to NannyBear
     * \param message   Message to send
     */
    public void write(String message){
        try{
            this.writer.write(message, 0, message.length());
            this.writer.newLine();
            this.writer.flush();
            myTimer.removeCallbacksAndMessages(null);
            timerConnection();
        }catch (IOException ex){
            Log.e("Connection", "Problem to send a message : "+ex.getMessage());
            changeConnectionState(false);
        }
    }

    /**
     * \brief Read in the buffer
     * \return The received message (\e string)
     */
    public String read(){
        String receivedMessage = null;
        try{
            receivedMessage = this.reader.readLine();
            if(receivedMessage.length()>0){
                myTimer.removeCallbacksAndMessages(null);
                timerConnection();
            }
        }catch(IOException ex){
            Log.e("Connection","Problem to read the message : "+ex.getMessage());
        }
        return receivedMessage;
    }

    /**
     * \brief Timer which send a message to test the connection every 5 seconds
     */
    private void timerConnection(){
        final Runnable testConnection = new Runnable() {
            @Override
            public void run() {
                getProxyRobot().sendAck();
            }
        };
        myTimer.postDelayed(testConnection, 5000);
    }

    /**
     * \brief Display a pop up for the lost of connection
     */
    void displayPopUp(){
        Log.e("Co","popUp");
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                int kindPopUp = 2; // connection
                String title = "CONNECTION PROBLEM";
                String message = "There is a problem with the connection.\nTo launch the connection click on the button Connection.\nOr you can continue offline and try to connect after with the settings tab";
                String button = "Connection";
                getActivity().getViewPuppeteer().displayPopUp(kindPopUp, title, message, button);
            }
        });
    }

    /**
     * \brief Change the connection status on screen
     * \param displayConnection The connection state
     */
    private void changeStateConnection(final boolean displayConnection){
        myHandler.post(new Runnable() {
            @Override
            public void run() {
                if ((activity.getSettingsScreen().getConnection_layout() != null) && (activity.getSettingsScreen().getConnection_layout().getVisibility() == View.VISIBLE)) {
                    activity.getViewPuppeteer().getmActivity().getSettingsScreen().changeConnectionStatus(displayConnection);
                }
                if(activity.getHomeScreen() != null && activity.getCurrentScreen() == activity.getHomeScreen()){
                    activity.getHomeScreen().changeConnectionStatus(displayConnection);
                }
                if(activity.getBabyStateScreen() != null && activity.getCurrentScreen() == activity.getBabyStateScreen()){
                    activity.getBabyStateScreen().changeConnectionStatus(displayConnection);
                }
            }
        });
    }

    /**
     * \brief Change the state of the connection and warn the user
     * \param connectionState   The connection state
     */
    void changeConnectionState(boolean connectionState){
        isConnected = connectionState;
        Log.e("Co", "connection = " + isConnected);
        if(!isConnected()){
            displayPopUp();
            if(activity.getTimeline().getmProgressItemsList().size() > 0) {
                activity.getTimeline().update("-1");
            }
            if(activity.getTemperature().getGraphic().getArrayListData().size() > 1) {
                activity.getTemperature().getGraphic().update(-50);
                activity.getHumidity().getGraphic().update(-50);
            }
            closeConnection();
        }
        changeStateConnection(isConnected);
    }

    /**
     * \brief Close timers
     */
    private void closeConnection(){
        myTimer.removeCallbacksAndMessages(null);
        activity.getViewPuppeteer().getCriticalityThresholds().getMyTimer().removeCallbacksAndMessages(null);
    }

    /*********** ACCESSORS ***********/
    /**
     * \brief Get method for connection state
     */
    public boolean isConnected() {
        return isConnected;
    }

    /**
     * \brief Get method for the socket
     */
    public Socket getSocket() {
        return socket;
    }

    /**
     * \brief Get method for the ProxyRobot class
     */
    public ProxyRobot getProxyRobot() {
        return proxyRobot;
    }

    /**
     * \brief Get method for the activity
     */
    public MainActivity getActivity() {
        return activity;
    }

    /**
     * \brief Get method for the Postman class
     */
    public Postman getPostman() {
        return postman;
    }

    /**
     * \brief Set method for the connection state
     */
    public void setConnected(boolean isConnected) {
        this.isConnected = isConnected;
    }

    /**
     * \brief Set method for buffer reader
     */
    public void setReader(BufferedReader reader) {
        this.reader = reader;
    }

    /**
     * \brief Set method for the buffer writer
     */
    public void setWriter(BufferedWriter writer) {
        this.writer = writer;
    }

    /**
     * \brief Set method for the ProxyRobot class
     */
    public void setProxyRobot(ProxyRobot proxyRobot) {
        this.proxyRobot = proxyRobot;
    }

    /**
     * \brief Set method for the Postman class
     */
    public void setPostman(Postman postman) {
        this.postman = postman;
    }

    /**
     * \brief Set method for the socket
     */
    public void setSocket(Socket socket) {
        this.socket = socket;
    }
}
